package org.nutz.ioc.java;

import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.LinkedArray;
import org.nutz.lang.util.LinkedCharArray;

public class ChainParsing {

	private char[] cs;
	private int i;
	private ChainNode first;
	private ChainNode last;
	/**
	 * 如果不为 null，那么 addNode 将全加到这里
	 */
	private LinkedArray<LinkedArray<ChainNode>> argss;
	/**
	 * 控制 parse 函数，合适退出
	 */
	private LinkedCharArray ends;
	/**
	 * parse 函数所使用的字符缓冲区
	 */
	private StringBuilder sb;

	public ChainParsing(String s) {
		ends = new LinkedCharArray(10);
		sb = new StringBuilder();
		argss = new LinkedArray<LinkedArray<ChainNode>>(5);
		cs = s.toCharArray();
		parse();
	}

	private void parse() {
		for (; i < cs.length; i++) {
			char c = cs[i];
			if (c == ',') {
				checkIfNeedAddNode();
			}
			// String
			else if (c == '\'' || c == '"') {
				clearStringBuffer();
				for (i++; i < cs.length; i++) {
					char n = cs[i];
					if (n == c)
						break;
					sb.append(n);
				}
				addNode(new StringNode(clearStringBuffer()));
			}
			// @Ioc | @Context | @Name
			else if (c == '@') {
				String name = readToDot().toUpperCase();
				if ("IOC".equals(name)) {
					addNode(new IocSelfNode());
				} else if ("CONTEXT".equals(name)) {
					addNode(new IocContextNode());
				} else if ("NAME".equals(name)) {
					addNode(new IocObjectNameNode());
				}
				continue;
			}
			// $xxx
			else if (c == '$') {
				String name = readToDot();
				addNode(new IocObjectNode(name));
				continue;
			}
			// (...) in func
			else if (c == '(') {
				String funcName = Strings.trim(clearStringBuffer());
				argss.push(new LinkedArray<ChainNode>(ChainNode.class, 5));
				ends.push(')');
				i++;
				parse();
				ends.popLast();
				ChainNode[] args = argss.popLast().toArray();
				int pos = funcName.lastIndexOf('.');
				if (pos > 0) {
					String className = funcName.substring(0, pos);
					funcName = funcName.substring(pos + 1);
					addNode(new StaticFunctionNode(className, funcName, args));
				} else {
					addNode(new ObjectFunctionNode(funcName, args));
				}
				clearStringBuffer();
			}
			// If funcs, it will will quit when encounter the ')'
			else if (ends.size() > 0 && c == ends.last()) {
				checkIfNeedAddNode();
				return;
			}
			// xxx.xxx.xxx
			else {
				sb.append(c);
			}
		}
		checkIfNeedAddNode();
	}

	private void checkIfNeedAddNode() {
		// Finished Parsing, check the buffer
		if (!Strings.isBlank(sb)) {
			String s = Strings.trim(clearStringBuffer());
			// null
			if (s.equalsIgnoreCase("null")) {
				addNode(new NullNode());
			}
			// boolean
			else if (s.matches("^(true|false)$")) {
				addNode(new BooleanNode(s));
			}
			// number
			else if (s.matches("^([0-9]+)$")) {
				addNode(new NumberNode(s));
			}
			// the chain is empty
			else if (null == last) {
				int pos = s.lastIndexOf('.');
				if (pos < 0)
					throw Lang.makeThrow("Don't know how to invoke '%s'", s);
				String className = s.substring(0, pos);
				String funcName = s.substring(pos + 1);
				addNode(new StaticFunctionNode(className, funcName, new ChainNode[0]));
			}
			// some node had been built
			else {
				addNode(new FieldNode(s));
			}
		}
	}

	private String readToDot() {
		for (i++; i < cs.length; i++) {
			char c = cs[i];
			if (c == '.' || c == ',')
				break;
			sb.append(c);
		}
		return clearStringBuffer();
	}

	private String clearStringBuffer() {
		String re = Strings.trim(sb);
		sb = new StringBuilder();
		return re;
	}

	private void addNode(ChainNode node) {
		// For arguments
		if (argss.size() > 0) {
			argss.last().push(node);
		}
		// First time
		else if (last == null) {
			first = node;
			last = node;
		}
		// Normal ...
		else {
			last.setNext(node);
			last = node;
		}
	}

	public ChainNode getNode() {
		return first;
	}

}
